//C#程序结构：https://docs.microsoft.com/zh-cn/dotnet/csharp/fundamentals/program-structure/
// 命名空间：
// 1. 设计目的是提供一种让一组名称与其他名称分隔开的方式
// 2. 一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突
// 3. 要使用命名空间中的类，要么通过using导入命名空间，要么写全称，特别是有名字冲突的时候
using System;

// 标识符：
// 1. 标识符是程序员为类、方法、变量等选择的名字
// 2. 标识符必须是一个完整的词，它由以字母和下划线开头的Unicode字符（字母、数字、@和下划线_）构成
// 3. 通常约定参数、局部变量以及私有字段应该以小写字母开头（例如myVariable），而其他类型的标识符则应该以大写字母开头（例如MyMethod）。

// 关键字：对编译器有特殊意义的名字
// 想要用关键字当标识符怎么办，加@

public class HelloWorld
{
    public static void Main()
    {
        // 变量：表示一个存储位置，存储特定的类型，运行过程中值可以改变
        // 常量：常量是一种值永远不会改变的静态字段。常量会在编译时静态赋值，编译器会在常量使用点上直接替换该值（类似于C++的宏）。
        
        int firstInteger = 0; // 变量
        int @int = 1; // 变量
        const double PI = 3.14; //常量，字母全部大小来命名


        Console.WriteLine("HELLO JSRUN     - from C# ");
        Console.WriteLine("firstInteger:  " + firstInteger.ToString());
        Console.WriteLine("@int:  " + @int);
        Hello.HelloWorld.print("Hello from Hello World");


        // 值类型与引用类型
        // 值类型
        // 1. 值类型的变量或常量的内容仅仅是一个值
        // 2. 值类型实例的赋值总是会进行实例**复制**
        // 3. 值类型实例占用的内存大小就是存储其字段所需的内存

        MyStruct.Point p1 = new MyStruct.Point();
        
        p1.X = 7;
       
        MyStruct.Point p2 = p1;
        
        Console.WriteLine($"p1.X is {p1.X}");
        Console.WriteLine($"p2.X is {p2.X}");

        p1.X = 9;

        Console.WriteLine($"p1.X is {p1.X}");
        Console.WriteLine($"p2.X is {p2.X}");

        // 引用类型
        // 1. 引用类型比值类型复杂，它由两部分组成：对象和对象引用
        // 2. 引用类型变量或常量中的内容是一个含值对象的引用
        // 3. 给引用类型变量赋值只会复制引用，而不是对象实例
        // 4. 引用可以赋值为字面量null，表示它并不指向任何对象
        // 5. 引用类型要求为引用和对象单独分配存储空间。对象除占用了和字段一样的字节数外，还需要额外的管理空间开销
        MyClass.Point pC1 = new MyClass.Point();
        
        pC1.X = 7;
       
        MyClass.Point pC2 = pC1;
        
        Console.WriteLine($"pC1.X is {pC1.X}");
        Console.WriteLine($"pC2.X is {pC2.X}");

        pC1.X = 9;

        Console.WriteLine($"p1.X is {pC1.X}");
        Console.WriteLine($"p2.X is {pC2.X}");





        // 预定义类型：
        // 1. 那些由编译器特别支持的类型
        // 2. 值类型： 数值、逻辑值、字符

        // 无符号整数
        Console.WriteLine(typeof(byte).Name + ":" + sizeof(byte));  
        Console.WriteLine(typeof(ushort).Name + ":" + sizeof(ushort)); 
        Console.WriteLine(typeof(uint).Name + ":" + sizeof(uint));  
        Console.WriteLine(typeof(ulong).Name + ":" + sizeof(ulong));   

        // 有符号整数
        Console.WriteLine(typeof(sbyte).Name + ":" + sizeof(sbyte));  
        Console.WriteLine(typeof(short).Name + ":" + sizeof(short)); 
        Console.WriteLine(typeof(int).Name + ":" + sizeof(int));  
        Console.WriteLine(typeof(long).Name + ":" + sizeof(long));   

        //实数
        Console.WriteLine(typeof(float).Name + ":" + sizeof(float));  
        Console.WriteLine(typeof(double).Name + ":" + sizeof(double)); 
        Console.WriteLine(typeof(decimal).Name + ":" + sizeof(decimal));  //decimal类型通常用于金融计算这种十进制下的高精度算术运算。

        //布尔类型
        Console.WriteLine(typeof(bool).Name + ":" + sizeof(bool));  

        // 字符类型:表示一个Unicode字符并占用两个字节
        Console.WriteLine(typeof(char).Name + ":" + sizeof(char));  
        char c = 'A';

        // 类型转换
        // 隐式类型转换：没有信息在转换过程中丢失
        // 显示类型转换：信息在转换过程中有可能丢失
        // 默认情况下，编译器将数值字面量推断为double类型或是整数类型
        var doubleVar = 1.0;
        Console.WriteLine("The type of doubleVar is  " + doubleVar.GetType().FullName);
        float floatVar = (float)doubleVar;

        //var隐式类型局部变量：如果编译器能够从初始化表达式中推断出变量的类型，就能够使用var关键字
        //字面量
        //数值后缀显式定义了字面量的类型
        var varVar = 1.0F;
        Console.WriteLine("1.0F: " + varVar.GetType().FullName);  
        var varVar1 = 1D;
        Console.WriteLine("1D: " + varVar1.GetType().FullName);  
        var varVar2 = 1.0M;
        Console.WriteLine("1.0M: " + varVar2.GetType().FullName);  
        var varVar3 = 1U;
        Console.WriteLine("1U: " + varVar3.GetType().FullName);  
        var varVar4 = -1L;
        Console.WriteLine("-1L: " + varVar4.GetType().FullName);    
        var varVar5 = 1UL;
        Console.WriteLine("1UL: " + varVar5.GetType().FullName);  

        

        

        // 3. 引用类型：字符串，数组，对象

        // 字符串：
        // 1. 不可变的Unicode字符序列
        // 2. string类型是引用类型而不是值类型。但是它的相等运算符却遵守值类型的语义
        // 3. +运算符可连接两个字符串
        // 4. 以$字符为前缀的字符串称为插值字符串。插值字符串可以在大括号内包含表达式
        string a = "test";        
        string b = "test";        
        Console.WriteLine ($" {a} == {b} is: " + (a == b));

        // 对象object
        // 1. object类型（System.Object）是所有类型的最终基类
        // 2. 任何类型都可以向上转换为object类型
        // 3. 装箱是将值类型实例转换为引用类型实例的行为
        int x = 9;
        object obj = x;

        Console.WriteLine ($"The type of x is {x.GetType().Name}. The value of x is {x}.");
        Console.WriteLine ($"The type of obj is {obj.GetType().Name}. The value of obj is {obj}.");
        // 4. 拆箱操作刚好相反，它把object类型转换成原始的值类型,拆箱需要显式类型转换
        int y = (int) obj;

        
        

        // 数组
        // 1. 数组是固定数量的特定类型的变量集合（称为元素）
        // 2. 为了实现高效访问，数组中的元素总是存储在连续的内存块中
        char[] vowels1 = {'a', 'e', 'i', 'o', 'u'};
        char[] vowels = new char[5];
        vowels[0] = 'a';
        vowels[1] = 'e';
        vowels[2] = 'i';
        vowels[3] = 'o';
        vowels[4] = 'u';

        Console.WriteLine($"vowels1[1] is {vowels1[1]}. vowels[1] is {vowels[1]}");

        // 数组元素的类型是值类型还是引用类型对其性能有重要的影响
        // 1. 若元素类型是值类型，每个元素的值将作为数组的一部分进行分配
        // 2. 若元素类型是引用，创建数组则仅仅分配了1000个空引用

        MyStruct.Point[] sPs = new MyStruct.Point[10];
        MyClass.Point[] cPs = new MyClass.Point[10];
        
        Console.WriteLine($"sPs[0].X is {sPs[0].X }. cPs[0].X is {cPs[0]?.X}");

        for(int i = 0; i < cPs.Length; i++) {
            cPs[i] = new MyClass.Point();
        }

        Console.WriteLine($"sPs[0].X is {sPs[0].X }. cPs[0].X is {cPs[0]?.X}");

        // 3. 多维数组
        // 3.1 矩形数组： 矩形数组声明时用逗号分隔每个维度
        int[, ] matrix = new int[3,3];
        for (int i = 0; i < matrix.GetLength(0); i++)
        {
            for (int j = 0; j < matrix.GetLength(1); j++)
            {
                matrix[i, j] = i * 3 + j;
            }
        }

        int[, ] matrix1 = new int[, ]        
        {          
            {0,1,2},          
            {3,4,5},          
            {6,7,8}        
        };  

        // 3.2 锯齿形数组： 锯齿形数组在声明时用一对方括号对表示每个维度 
        // 不同于矩形数组，锯齿形数组内层维度在声明时并未指定。
        // 每个内层数组都可以是任意长度  
        // 每一个内层数组都隐式初始化为null而不是一个空数组，因此都需要手动创建
        int[][] matrix2 = new int[3][];
        for (int i = 0; i < matrix2.Length; i++)
        {
            matrix2[i] = new int[i+1];
            for (int j = 0; j < matrix2[i].Length; j++)
            {
                matrix2[i][j] = i * 3 + j;
            }
        }

        int[][] matrix3 = new int[][] 
        {
            new int[] {0},
            new int[] {3,4},
            new int[] {6,7,8}
        };
             
                    



        // 自定义类型
        // 类，结构体，枚举

        // 方法
        
        Console.WriteLine($"30 Feet equals to {FeetToInches(30)} Inches");
     
        Console.WriteLine($"100 Feet equals to {FeetToInches(100)} Inches");

        // ref: 传引用，不仅可以改变函数外的值，还能将函数内部的值返回
        int p = 3;
        Console.WriteLine($"p in Main is {p}");
        Foo1(ref p);
        Console.WriteLine($"p in Main is {p}");

        // out: 传引用，用于返回函数内部的值,不要对传入的值赋值
        int q;
        //Console.WriteLine($"q in Main is {q}");
        Foo2(out q);
        Console.WriteLine($"q in Main is {q}");

        // 委托
        DelegateType.Del firsthandler = DelegateType.DelegateMethod.First;

        firsthandler("One hanlder");

        DelegateType.Del secondHandler = DelegateType.DelegateMethod.Second;

        DelegateType.Del twoHandler = firsthandler + secondHandler;

        twoHandler("Two hanlder");
   
    }

    // 方法
    // 创建方法的目的：代码的复用
    // 方法的签名：方法的签名由它的名字和一定顺序的参数类型（但不包含参数名和返回值类型）组成 +  参数按值传递还是按引用传递

    static int FeetToInches(int feet) 
    {
        int inches = feet * 12;
       
        return inches;
    }

    // ref: 传引用，不仅可以改变函数外的值，还能将函数内部的值返回
    static void Foo1(ref int p) {
        p = p + 1;
        Console.WriteLine($"p in Foo1 is {p}");
    }

    // out: 传引用，用于返回函数内部的值,不要对传入的值赋值
    static void Foo2(out int q) {
        q = 3;
        Console.WriteLine($"q in Foo2 is {q}");
    }


}

// 委托
// 委托是一种引用类型，表示对具有特定参数列表和返回类型的方法的引用
// 在实例化委托时，你可以将其实例与任何具有兼容签名和返回类型的方法相关联
// 可以通过委托实例调用方法
// 委托用于将方法作为参数传递给其他方法
// 事件处理程序就是通过委托调用的方法
// 委托类似于 C++ 函数指针，但委托完全面向对象，不像 C++ 指针会记住函数，委托会同时封装对象实例和方法。
//委托允许将方法作为参数进行传递。
//委托可用于定义回调方法。
//委托可以链接在一起；例如，可以对一个事件调用多个方法。

namespace DelegateType {

    public delegate void Del(string message);

    public class DelegateMethod
    {
         public static void First(string message)
        {
            Console.WriteLine("First: " + message);
        }

        public static void Second(string message)
        {
            Console.WriteLine("Second: " + message);
        }
    }

}

namespace Hello
{
    public class HelloWorld{
        public static void print(string s) 
        {
            Console.WriteLine(s);
        }
    }
    
}

namespace MyClass
{
    // 类
    // 1. 用class声明的类型
    // 2. C#面向对象编程的基本单位
    // 3. 对某一类事物属性和行为的封装
    class Point
    {
        public int X, Y; // 字段： 类或结构体中的变量成员
    }

    class PropertyPoint
    {
        // 属性：从外表看来，属性（Property）和字段很类似，但是属性内部像方法一样含有逻辑
        // 属性是一个值类型，它返回的是值类型的副本，不是值类型本身
        public int X {get;set;}
        public int Y {get;set;}
        /*public string PointValue {
            get {
                return $"({this.X}, {this.Y})";
            }
        }*/
    }

    // 面向对象：https://docs.microsoft.com/zh-cn/dotnet/csharp/fundamentals/tutorials/oop
    // 继承:https://docs.microsoft.com/zh-cn/dotnet/csharp/fundamentals/object-oriented/inheritance
    // 多态:https://docs.microsoft.com/zh-cn/dotnet/csharp/fundamentals/object-oriented/polymorphism
    // 接口
}

namespace MyStruct
{
    // 结构体
    // 1. 与类类似，但是是值类型，不是引用类型
    // 2. 结构体隐式包含一个无法重写的无参数构造器，将字段按位置为0
    // 3. 定义结构体的构造器时，必须显式为每一个字段赋值
    public struct Point 
    {
        public int X, Y;// 字段： 类或结构体中的变量成员
    }

    public struct PrivatePoint 
    {
       int x, y;

       public PrivatePoint(int x, int y) {
           this.x = x;
           this.y = y;
       }
    }
}

